package com.zzedu.framework.aspectj;

import com.zzedu.common.annotation.DataScope;
import com.zzedu.common.core.domain.entity.SysRole;
import com.zzedu.common.core.domain.entity.SysUser;
import com.zzedu.common.utils.SecurityUtils;
import com.zzedu.common.utils.StringUtils;
import com.zzedu.framework.interceptor.DataFilterScopeContext;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Function;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.SelectExpressionItem;
import net.sf.jsqlparser.statement.select.SubSelect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Collections;

/**
 * 数据过滤处理
 *
 * @author smarthome
 */
@Aspect
@Component
public class DataScopeAspect {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    /**
     * 全部数据权限
     */
    public static final String DATA_SCOPE_ALL = "1";

    /**
     * 自定数据权限
     */
    public static final String DATA_SCOPE_CUSTOM = "2";

    /**
     * 部门数据权限
     */
    public static final String DATA_SCOPE_DEPT = "3";

    /**
     * 部门及以下数据权限
     */
    public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";

    /**
     * 仅本人数据权限
     */
    public static final String DATA_SCOPE_SELF = "5";

    @Pointcut("@annotation(com.zzedu.common.annotation.DataScope)")
    public void pointCut() {
    }

    @Before(value = "pointCut()")
    public void doBefore(JoinPoint point) throws Throwable {
        DataScope dataScope = null;
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        dataScope = AnnotationUtils.findAnnotation(method, DataScope.class);
        if (null == dataScope) {
            dataScope = AnnotationUtils.findAnnotation(method.getDeclaringClass(), DataScope.class);
        }
        if (null != dataScope) {
            handleDataScope(point, dataScope);
        }

    }

    @AfterReturning(pointcut = "pointCut()")// returning的值和doAfterReturning的参数名一致
    public void doAfterReturning(JoinPoint point) throws Throwable {
        DataFilterScopeContext.clearDataFilterScope();
    }

    @AfterThrowing(pointcut = "pointCut()", throwing = "e")// returning的值和doAfterReturning的参数名一致
    public void doAfterReturning(JoinPoint point, Throwable e) throws Throwable {
        DataFilterScopeContext.clearDataFilterScope();
    }

    protected void handleDataScope(final JoinPoint joinPoint, DataScope dataScope) {
        try {
            // 获取当前的用户
            SysUser currentUser = SecurityUtils.getSysUser();
            if (currentUser != null) {
                // 如果是超级管理员，则不过滤数据
                if (!currentUser.isAdmin()) {
                    DataFilterScopeContext.setDataFilterScope(dataScopeFilter(currentUser, dataScope));
                }
            }
        } catch (Exception e) {
            logger.error("构造权限过滤异常:{}", e.getMessage());
        }
    }

    /**
     * 构建过滤条件
     *
     * @param user  当前登录用户
     * @param where 当前查询条件
     * @return 构建后查询条件
     */
    public Expression dataScopeFilter(SysUser user, DataScope filterDataScope) {
        Expression expression = null;
        for (SysRole role : user.getRoles()) {
            String dataScope = String.valueOf(role.getDataScope());
            if (DataScopeAspect.DATA_SCOPE_ALL.equals(dataScope)) {
                return null;
            } else if (DataScopeAspect.DATA_SCOPE_CUSTOM.equals(dataScope)) {
                InExpression inExpression = new InExpression();
                inExpression.setLeftExpression(buildColumn(filterDataScope.deptAlias(), filterDataScope.deptIdColumnName()));
                SubSelect subSelect = new SubSelect();
                PlainSelect select = new PlainSelect();
                select.setSelectItems(Collections.singletonList(new SelectExpressionItem(new Column(filterDataScope.deptIdColumnName()))));
                select.setFromItem(new Table("sys_role_dept"));
                EqualsTo equalsTo = new EqualsTo();
                equalsTo.setLeftExpression(new Column("role_id"));
                equalsTo.setRightExpression(new LongValue(role.getId()));
                select.setWhere(equalsTo);
                subSelect.setSelectBody(select);
                inExpression.setRightExpression(subSelect);
                expression = ObjectUtils.isNotEmpty(expression) ? new OrExpression(expression, inExpression) : inExpression;
            } else if (DataScopeAspect.DATA_SCOPE_DEPT.equals(dataScope)) {
                EqualsTo equalsTo = new EqualsTo();
                equalsTo.setLeftExpression(buildColumn(filterDataScope.deptAlias(), filterDataScope.deptIdColumnName()));
                equalsTo.setRightExpression(new LongValue(user.getDeptId()));
                expression = ObjectUtils.isNotEmpty(expression) ? new OrExpression(expression, equalsTo) : equalsTo;
            } else if (DataScopeAspect.DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) {
                InExpression inExpression = new InExpression();
                inExpression.setLeftExpression(buildColumn(filterDataScope.deptAlias(), filterDataScope.deptIdColumnName()));
                SubSelect subSelect = new SubSelect();
                PlainSelect select = new PlainSelect();
                select.setSelectItems(Collections.singletonList(new SelectExpressionItem(new Column(filterDataScope.deptIdColumnName()))));
                select.setFromItem(new Table("sys_dept"));
                EqualsTo equalsTo = new EqualsTo();
                equalsTo.setLeftExpression(new Column(filterDataScope.deptIdColumnName()));
                equalsTo.setRightExpression(new LongValue(user.getDeptId()));
                Function function = new Function();
                function.setName("find_in_set");
                function.setParameters(new ExpressionList(new LongValue(user.getDeptId()), new Column("ancestors")));
                select.setWhere(new OrExpression(equalsTo, function));
                subSelect.setSelectBody(select);
                inExpression.setRightExpression(subSelect);
                expression = ObjectUtils.isNotEmpty(expression) ? new OrExpression(expression, inExpression) : inExpression;
            } else if (DataScopeAspect.DATA_SCOPE_SELF.equals(dataScope)) {
                EqualsTo equalsTo = new EqualsTo();
                equalsTo.setLeftExpression(buildColumn(filterDataScope.userAlias(), filterDataScope.userIdColumnName()));
                equalsTo.setRightExpression(new LongValue(user.getId()));
                expression = ObjectUtils.isNotEmpty(expression) ? new OrExpression(expression, equalsTo) : equalsTo;
            }
        }
        return expression;
    }

    /**
     * 构建Column
     *
     * @param tableAlias 表别名
     * @param columnName 字段名称
     * @return 带表别名字段
     */
    public Column buildColumn(String tableAlias, String columnName) {
        if (StringUtils.isNotEmpty(tableAlias)) {
            columnName = tableAlias + "." + columnName;
        }
        return new Column(columnName);
    }
}
